package it.uniba.di.mas.jpnde.core;

import jade.util.leap.ArrayList;
import jade.util.leap.List;
import jade.util.leap.Serializable;

public class Place extends PNObject
	implements Serializable, Cloneable{
	
	private static final long serialVersionUID = 1L;
	public static final String XMLNODE="place";
	public static final String XMLINITMARK_NODE="initialMarking";
	public static final String XMLEVENTIN_NODE="jpneventin";
	public static final String XMLEVENTOUT_NODE="jpneventout";
	public static final String XMLENDPLACE_NODE="jpnendplace";
	
	private PlaceEvent event=null;
	private String endPlace="N";
	private long initialMark=0;
	private long endThreshold=0;
	private ArrayList tokens=new ArrayList();
	
	private long numToken=0;
	
	public String getEndPlace() {
		return endPlace;
	}
	public void setEndPlace(String endPlace) {
		this.endPlace = endPlace;
	}
	public long getInitialMark() {
		return initialMark;
	}
	public void setInitialMark(long mark) {
		this.initialMark = mark;
	}
	public long getEndThreshold() {
		return endThreshold;
	}
	public void setEndThreshold(long endThreshold) {
		this.endThreshold = endThreshold;
	}
	
	public boolean isEndPlace(){
		return endPlace.equalsIgnoreCase("Y");
	}
	
	public PlaceEvent getEvent() {
		return event;
	}
	public void setEvent(PlaceEvent event) {
		this.event = event;
	}
	
	public void initTokens()
		throws Exception
	{
		tokens.clear();
		for (int i=0;i<initialMark;i++){
			addToken(new Token(id));
		}
		
		if (getScript()!=null &&
			getScript().existFunction(ScriptFunction.ON_INITIAL_MARKING))
			{
				List vars=new ArrayList();
				Token[] vTokens=new Token[tokens.size()];
				for(int i=0;i<tokens.size();i++)
					vTokens[i]=(Token)tokens.get(i);
				vars.add(new TaggedValue("tokens",vTokens));
				getScript().execute(ScriptFunction.ON_INITIAL_MARKING,vars);
			}
	}
	
	public long tokensCount(){ 
		return tokens.size();
	}
	
	public synchronized void addToken(Token t)
		throws Exception
	{
		numToken++;
		t.setId(id+"_"+numToken);
		t.setPlaceId(id);
		
		if (getScript()!=null &&
			getScript().existFunction(ScriptFunction.ON_ADD))
			{
				List vars=new ArrayList();
				vars.add(new TaggedValue("token",t));
				getScript().execute(ScriptFunction.ON_ADD,vars);
				t=(Token)getScript().getReaturnValues().get("token");
			}
		if (t!=null)
			tokens.add(t);
	}
	
	public synchronized Token getOutToken(){
		Token token=null;
		if (tokensCount()>0){
			token=(Token)tokens.get(0);
			tokens.remove(0);
			}
		return token;
	}
	
	public synchronized void removeToken(Token t)
		throws Exception
	{
		if (!tokens.contains(t))
			throw new Exception("Place.removeToken():Token <"+t.getId()+"> not found !");
		tokens.remove(t);	
	}
	
	public boolean isInEndState(){
		if (isEndPlace()&&tokensCount()>=endThreshold)
			return true;
		else
			return false;
	}
	
	public synchronized List getTokens(Arc arc, List vars)
		throws Exception
	{
		List ret=new ArrayList();
	
		if (tokensCount()>=arc.getWeight()){
			if (arc.getCondition()==null)
				{
					for(int i=0;i<arc.getWeight();i++)
						ret.add(tokens.get(i));
				}
			else
				{	
					for(int i=0;i<arc.getWeight();i++){
						ScriptCondition cond=arc.getCondition();
						boolean eval=false;
						for(int j=0;j<tokens.size();j++){
							Token token=(Token)tokens.get(j);
							if (ret.contains(token))
								continue;
							try {
								eval=cond.eval(i, token, vars);
								if (eval)
									{
									ret.add(token);
									break;
									}
							} catch (Exception e) {
								throw new Exception("Place.getTokens():"+e.getMessage());
							}
						}
						if (!eval){
							ret=new ArrayList();
							break;
						}
					}
				}
		} //tokensCount
		
		return ret;
	}
	
	@Override
	public String toString() {
		return "Place [id=" + id + ", name=" + name
				+ ", #Tokens=" + tokensCount()
				+ ", initialMark=" + initialMark  
				+ ", event=" + event + ", endPlace=" + endPlace
				+ ", endThreshold=" + endThreshold
				+ "]";
	}
	
	protected void setTokens(ArrayList tokens){
		this.tokens=tokens;
	}
	
	public Object clone()
	{
		Place ret=new Place();
		ret.setId(id);
		ret.setName(name);
		ret.setEndPlace(endPlace);
		ret.setInitialMark(initialMark);
		ret.setEndThreshold(endThreshold);
		ret.setEvent(event);
		ret.setTokens((ArrayList)tokens.clone());
		return ret;
	}
}
